#include <Arduino.h>
#include <FastLED.h>
#include <WiFi.h>
#include <WebServer.h>
#include <ArduinoJson.h>
#include <EEPROM.h>
// #include <ESP8266WiFi.h>
// #include <ESP8266WebServer.h>


// 定义引脚和灯珠数量
#define RGB_LED_PIN     2
#define NUM_LEDS    1

// 定义按键引脚
const int buttonPin = 9;  // boot
  
// GPIO pin connected to LED  
const int ledPin = 12;  
const int wifiPin = 13;  

// 变量用于存储按键状态
int buttonState = -1;

// WIFI:T:WPA;P:5sb4z3u9;S:CU_6Fty;H:true;
// 外部WiFi
char ssid[32];     // WiFi名称
char password[64]; // WiFi密码

// 本机热点
const char* APSsid = "ESP32_C3_AP";  
const char* APPsword = "12345678"; 

int h =0;
int s =255;
int v =255;

// 定义颜色变量
CRGB leds[NUM_LEDS];

// Web server port  
const uint16_t port = 80;  
  
// Create WebServer object on port 80  
WebServer server(80);  


bool setWifiState = true;  // 是否需要初始设置WiFi
  
// Variable to store the current LED state  
bool ledState = true;  
int wifiState = HIGH;  
  
// HTML page content  
const char* stateControllerHtml = R"rawliteral( 
<!DOCTYPE html><html><head><meta charset="utf-8"><title>ESP32-C3 LED 控制器</title><style>body{font-family:Arial,sans-serif;text-align:center;margin-top:50px}button{padding:10px 20px;font-size:16px}</style></head><body><h1>ESP32-C3 LED 控制器</h1><p id="ledStatus">LED的状态：%(led_status)s</p><button onclick="fetch('/toggle').then(response => response.text()).then(data => {document.getElementById('ledStatus').innerText = 'LED的状态： ' + data})">切换LED状态</button></body></html>
)rawliteral";  

const char* colorPickerHtml = R"rawliteral(
<!DOCTYPE html><html lang="zh"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>HSV颜色取色器</title><style>#color-picker {width: 300px;height: 300px;border: 1px solid #ccc;display: flex;align-items: center;justify-content: center;cursor: pointer;margin-right: 50px;}#hue-slider, #saturation-slider, #value-slider {width: 300px;}#body-div {display: flex;flex-direction: row; justify-content: center;align-items: center;height: 100%; }</style></head><body><div id="body-div"><div id="color-picker"></div><div><label for="hue-slider">颜色：</label><input type="range" id="hue-slider" min="0" max="255" value="0"><br><br><label for="saturation-slider">饱和：</label><input type="range" id="saturation-slider" min="0" max="255" value="255"><br><br><label for="value-slider">亮度：</label><input type="range" id="value-slider" min="0" max="255" value="255"></div></div><script>const colorPicker = document.getElementById('color-picker');const hueSlider = document.getElementById('hue-slider');const saturationSlider = document.getElementById('saturation-slider');const valueSlider = document.getElementById('value-slider');function updateColor() {const hue = parseInt(hueSlider.value) / 255;const saturation = parseInt(saturationSlider.value) / 255;const value = parseInt(valueSlider.value) / 255;const rgb = hsvToRgb(hue, saturation, value);console.log("hsvToRgb=h=",parseInt(hueSlider.value),'=s=',  parseInt(saturationSlider.value),'=v=',  parseInt(valueSlider.value));console.log("rgb==", rgb);colorPicker.style.backgroundColor = `rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`;const requestBody = {"h": parseInt(hueSlider.value),"s": parseInt(saturationSlider.value),"v": parseInt(valueSlider.value)};const jsonBody = JSON.stringify(requestBody);console.log("jsonBody==", jsonBody);const url = '/setColor'; sendPostRequest(url,jsonBody);}function hsvToRgb(h, s, v) {console.log("hsv1==", h,'==', s,'==', v);let r, g, b;let i = Math.floor(h * 6);console.log("i % 6 ==", i);let f = h * 6 - i;let p = v * (1 - s);let q = v * (1 - f * s);let t = v * (1 - (1 - f) * s);console.log("i % 6 ==", i % 6);switch (i % 6) {case 0:r = v;g = t;b = p;break;case 1:r = q;g = v;b = p;break;case 2:r = p;g = v;b = t;break;case 3:r = p;g = q;b = v;break;case 4:r = t;g = p;b = v;break;case 5:r = v;g = p;b = q;break;}console.log("hsv==", h,'==', s,'==', v);console.log("RGB==", r,'==', g,'==', b);return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];}hueSlider.addEventListener('input', updateColor);saturationSlider.addEventListener('input', updateColor);valueSlider.addEventListener('input', updateColor);async function sendPostRequest(url,jsonBody) {try {const response = await fetch(url, {method: 'POST', headers: {'Content-Type': 'application/json' },body: jsonBody });if (!response.ok) {throw new Error(`HTTP error! Status: ${response.status}`);}const data = await response.text();console.log('Success:', data);} catch (error) {console.error('Error:', error);}}window.onload = function() {const xhr = new XMLHttpRequest();xhr.open("GET", "/initHtml", true);xhr.onreadystatechange = function() {if (xhr.readyState === 4 && xhr.status === 200) {console.log('xhr.responseText===',xhr.responseText);const result = JSON.parse(xhr.responseText);hueSlider.value = result.h;saturationSlider.value = result.s;valueSlider.value = result.v;updateColor();console.log('xhr.result===',result);}};xhr.send();};</script></body></html>
)rawliteral";  

const char* setWiFiHtml = R"rawliteral(
<!DOCTYPE html><html lang="zh"><head><meta charset="UTF-8"><title>ESP32c3_AP WiFi设置</title><style>body{font-family:Arial,sans-serif;display:flex;justify-content:center;align-items:center;height:100vh;margin:0}label{display:block;margin-bottom:10px}input[type="text"],input[type="password"]{width:92%;padding:8px;margin-top:5px;border:1px solid #ccc;border-radius:5px}input[type="submit"]{width:100%;padding:10px;background-color:#4CAF50;color:white;border:none;border-radius:5px;cursor:pointer}</style></head><body><form action="/setWiFi" method="post"><label for="ssid">SSID: </label><input type="text" id="ssid" name="ssid"><br><br><label for="password">Password:<input type="password" id="password" name="password"></label><br><input type="submit" value="Connect"></form></body></html>
)rawliteral";  

const char* routeHtml = R"rawliteral(
<!DOCTYPE html><html lang="zh"><head><meta charset="UTF-8"><title>WiFi连接成功！</title></head><body><center><h2>功能体验</h2><br><a href="%(IP_ADDRESS)s">IP 地址:%(IP_ADDRESS)s</a><br><a href="stateControl">LED 控制页面</a><br><br><a href="colorPicker">RGB 颜色控制页面</a><br><br><a href="/">设置WiFi页面</a></center></body></html>
)rawliteral";  

void route() {
  // 构建一个HTML表单，用户可以输入SSID和密码进行连接
  String htmlContent = String(routeHtml);  
  IPAddress ip = WiFi.localIP();
  String ipStr = String(ip[0]) + "." + String(ip[1]) + "." + String(ip[2]) + "." + String(ip[3]);
  htmlContent.replace("%(IP_ADDRESS)s", ipStr);
  server.send(200, "text/html", htmlContent); // 发送HTML内容给客户端
}

void setWiFiPage() {
  // 构建一个HTML表单，用户可以输入SSID和密码进行连接
  String htmlContent = String(setWiFiHtml);  
  server.send(200, "text/html", htmlContent); // 发送HTML内容给客户端
}
  
void stateController() {  
    // Generate HTML content with current LED status  
    String htmlContent = String(stateControllerHtml);  
    htmlContent.replace("%(led_status)s", ledState ? "ON" : "OFF");  
    Serial.print("LED status：");  
    Serial.println(ledState ? "ON" : "OFF");  
    server.send(200, "text/html", htmlContent);  
    // server.send(200, "text/html;charset=utf-8", htmlContent);  
}  
void colorPicker() {  
    String htmlContent = String(colorPickerHtml);  
    server.send(200, "text/html", htmlContent);  
}  
void initHtml() {  
    std::string json = "{\"h\": " + std::to_string(h) + ", \"s\": " + std::to_string(s) + ", \"v\": " + std::to_string(v) + "}";
    // server.send(200, "application/json", json); 
    server.send(200, "application/json", String(json.c_str()));
}  
  
void handleToggle() {  
    // Toggle LED state  
    ledState = !ledState;  
    digitalWrite(ledPin, ledState);  
    Serial.print("LED status：");  
    Serial.println(ledState ? "ON" : "OFF");  
    // Send response with new LED status  
    server.send(200, "text/plain", ledState ? "ON" : "OFF");  
}

void setColor() { 
    String requestBody = server.arg("plain");
    JsonDocument doc;
    deserializeJson(doc, requestBody);

    h = doc["h"];
    s = doc["s"];
    v = doc["v"];
    fill_solid(leds, NUM_LEDS, CHSV(h, s, v));
    FastLED.show();
    Serial.print("Received data: h=");
    Serial.print(h);
    Serial.print(", s=");
    Serial.print(s);
    Serial.print(", v=");
    Serial.println(v);

    server.send(200, "text/plain", "Data received and processed");
 }

void clearEEPROM(){
    // 获取EEPROM的大小
  if (EEPROM.read(0)==0xFF||EEPROM.read(0)==0)// 当指定地址没有数据时，EEPROM.read(address) 会返回 0xFF（即 255）, 表示该地址上没有有效的数据
  {
    return;
  }
  
  // 遍历EEPROM的每一个字节并将其设置为0 硬擦除
  // for (int i = 0; i < strlen(ssid) + strlen(password) + 3; i++) {
  //   EEPROM.write(i, 0);
  // }
  EEPROM.write(0, 0); // 软擦除
  // 提交更改
  EEPROM.commit();
  Serial.println("EEPROM has been cleared!");
}
 
void saveWifiInfo(){
  int index = 0;
  EEPROM.write(index++, 1);  // 将0位作为标识符，1则有数据0则无数据
  // 将SSID写入EEPROM地址0
  for (int i = 0; i < strlen(ssid); ++i) {
    EEPROM.write(i+index, ssid[i]);
  }
  index += strlen(ssid);
  EEPROM.write(index++, '\0'); // 添加字符串结束符
  // 将密码写入EEPROM地址10（假设SSID长度不超过10）
  for (int i = 0; i < strlen(password); ++i) {
    EEPROM.write(index + i, password[i]);
  }
  index += strlen(password);
  EEPROM.write(index, '\0'); // 添加字符串结束符

  if (EEPROM.commit()) {
    Serial.println("Data saved to EEPROM successfully.");
  } else {
    Serial.println("Failed to save data to EEPROM.");
  }

}

bool readWifiInfo(){
  if (EEPROM.read(0)==0xFF||EEPROM.read(0)==0)// 当指定地址没有数据时，EEPROM.read(address) 会返回 0xFF（即 255）, 表示该地址上没有有效的数据
  {
    return false;
  }
  
  // 读取SSID
  for (int i = 0; i < sizeof(ssid); ++i) {
    ssid[i] = EEPROM.read(i);
    if (ssid[i] == '\0') break; // 如果遇到字符串结束符则停止读取
  }

  // 读取Password
  int startAddress = strlen(ssid) + 1; // 从SSID的结束符后开始
  for (int i = 0; i < sizeof(password); ++i) {
    password[i] = EEPROM.read(startAddress + i);
    if (password[i] == '\0') break; // 如果遇到字符串结束符则停止读取
  }
  return true;
}

bool setWiFi(){
    Serial.print("Connecting to "); // 在串口输出提示信息
    Serial.print(ssid); // 输出SSID
    Serial.print(" with password "); // 输出提示信息
    Serial.println(password); // 输出密码
    // for(int i = 0; i < 10; i++) {
        // Serial.print("第");    
        // Serial.print(i+1);    
        // Serial.println("次尝试连接！");    
        /**
        * WiFi.mode() 是一个设置ESP32-C3 Wi-Fi工作模式的函数。它接受一个参数，该参数指定了Wi-Fi的工作模式。常见的模式有：
        * WIFI_OFF: 关闭Wi-Fi功能。
        * WIFI_STA: 仅作为Station（客户端）模式运行，连接到现有的Wi-Fi网络。
        * WIFI_AP: 仅作为Access Point（热点）模式运行，创建一个Wi-Fi网络供其他设备连接。
        * WIFI_AP_STA: 同时作为Station和Access Point模式运行，既可以连接到现有的Wi-Fi网络，也可以创建自己的Wi-Fi网络。
        * WiFi.mode(WIFI_STA);
        * */
        WiFi.begin(ssid, password); // 尝试连接到指定的WiFi网络
        while (WiFi.status() != WL_CONNECTED) {  
            digitalWrite(wifiPin, wifiState);  
            Serial.println("等待连接WiFi...");  
            delay(500);  
            wifiState = wifiState==HIGH?LOW:HIGH;
        }  
        if (WiFi.status() == WL_CONNECTED)
        {                                                         // 检查是否成功连接到WiFi网络
          Serial.println("WiFi连接成功！");                       // 在串口输出连接成功的信息
          analogWrite(wifiPin, 200);
          Serial.println("Connected to WiFi");
          Serial.print("\nIPv4地址：");
          Serial.println(WiFi.localIP());
          return true;
        }
    // }
    Serial.println("WiFi连接失败！");                 // 在串口输出连接失败的信息
    return false;
}

void setWiFiServer() {
  // 检查是否有SSID和密码参数
  if (server.hasArg("ssid") && server.hasArg("password")) {
    String newSSID = server.arg("ssid"); // 获取用户输入的SSID
    String newPassword = server.arg("password"); // 获取用户输入的密码

    strcpy(ssid, newSSID.c_str());
    strcpy(password, newPassword.c_str());
    
    if (setWiFi())
    {
      saveWifiInfo();
      setWifiState = false;
      // String(routeHtml)
      // server.send(200, "text/plain", "Connected to WiFi!");   // 向客户端发送连接成功的响应
      route();
    }else{
      server.send(200, "text/plain", "Failed to connect to WiFi!"); // 向客户端发送连接失败的响应
    }
  } else {
    server.send(400, "text/plain", "Invalid request"); // 如果请求无效，返回400错误码
  }
}

void openWifiAP(){
  digitalWrite(ledPin, ledState);
    WiFi.mode(WIFI_AP);              // 设置ESP32为接入点模式（热点模式）
    WiFi.softAP(APSsid, APPsword);   // 启动热点，使用指定的SSID和密码
    delay(1500);                     // 延时一秒再打印
    Serial.print("AP IP address: "); // 在串口输出提示信息
    Serial.println(WiFi.softAPIP()); // 获取并输出ESP32的IP地址
}

void setup()
{
  // Initialize Serial Monitor
  Serial.begin(115200); // 初始化串口通信，波特率为115200
  EEPROM.begin(512);    // 初始化EEPROM，大小为512字节

  FastLED.addLeds<WS2812, RGB_LED_PIN, GRB>(leds, NUM_LEDS);
  FastLED.setBrightness(50); // 设置亮度为50%
  Serial.println("初始化FastLED库");
  // Initialize LED pin as OUTPUT
  pinMode(ledPin, OUTPUT);
  pinMode(wifiPin, OUTPUT);
  // 设置按键引脚为输入模式
  pinMode(buttonPin, INPUT_PULLUP);

  // if (EEPROM.read(0) == '\0')
  if (setWifiState || !readWifiInfo())
  {
    openWifiAP(); // 打开热点以配置WiFi
  }
  else
  {
    setWiFi();  // 连接WiFi
  }

  // // Connect to WiFi
  // WiFi.mode(WIFI_STA);
  // WiFi.begin(ssid, password);
  // while (WiFi.status() != WL_CONNECTED) {
  //     digitalWrite(wifiPin, wifiState);
  //     Serial.println("Connecting to WiFi...");
  //     delay(500);
  //     wifiState = wifiState==HIGH?LOW:HIGH;
  // }
  // digitalWrite(ledPin, LOW);
  server.on("/", setWiFiPage);          // 设置WiFi页面
  server.on("/route", route);           // 路由
  server.on("/setWiFi", setWiFiServer); // 设置WiFi接口
  // Start server
  server.on("/stateControl", stateController); // led 控制页面
  server.on("/toggle", handleToggle);          // led 控制接口
  server.on("/colorPicker", colorPicker);      // 控制rbg颜色页面
  server.on("/initHtml", initHtml);            // 初始化rbg颜色页面接口

  server.on("/setColor", HTTP_POST, setColor); // 设置rbg颜色页面接口
  server.begin();
  Serial.println("HTTP server started");
}

void loop() {  
  server.handleClient();  
    // 读取按键状态
  buttonState = digitalRead(buttonPin);
  
  // 检查按键是否被按下（低电平）
  if (buttonState == LOW) {
    // 打印消息到串口监视器
    Serial.println("清除已保存数据并重新设置WiFi");
    clearEEPROM();
    openWifiAP(); // 打开热点以配置WiFi
    buttonState=-1;
    // 防止抖动，延时一段时间
    delay(500);
  }
}  